package com.nbcio.iot.controller;

import com.nbcio.common.core.domain.R;
import com.nbcio.iot.config.IotModbusProperties;
import com.nbcio.iot.domain.vo.IotDeviceVo;
import com.nbcio.iot.domain.vo.IotProductVo;
import com.nbcio.iot.protocol.modbus.ModbusManager;
import com.nbcio.iot.service.IIotDeviceService;
import com.nbcio.iot.service.IIotProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/iot/modbus")
@RequiredArgsConstructor
public class ModbusController {

    private final ModbusManager modbusManager;
    private final IotModbusProperties properties;
    private final IIotDeviceService deviceService;
    private final IIotProductService productService;

    @GetMapping("/tcp/readHolding")
    public R<Number> tcpRead(@RequestParam int slaveId, @RequestParam int offset,
                              @RequestParam(required = false) Long deviceId,
                              @RequestParam(required = false) Long productId,
                              @RequestParam(required = false) Integer port,
                              @RequestParam(required = false) String host,
                              @RequestParam(required = false) Integer timeoutMs) throws Exception {
        // 优先使用前端传入的 host/port/timeout 便于快速测试
        if (StringUtils.hasText(host) && port != null) {
            int tm = timeoutMs != null ? timeoutMs : 2000;
            return R.ok(modbusManager.readHoldingRegisterTcp(host, port, tm, slaveId, offset));
        }
        // 其次使用 设备 > 产品 的 JSON 配置
        Map<String, Object> cfg = resolveModbusTcpConfig(deviceId, productId);
        if (cfg != null) {
            String h = (String) cfg.getOrDefault("host", properties.getTcp().getHost());
            int p = asInt(cfg.get("port"), properties.getTcp().getPort());
            int tm = asInt(cfg.get("timeoutMs"), properties.getTcp().getTimeoutMs());
            return R.ok(modbusManager.readHoldingRegisterTcp(h, p, tm, slaveId, offset));
        }
        // 最后回退 YAML
        if (!properties.getTcp().isEnabled()) {
            return R.fail("Modbus TCP 未启用, 请在配置 iot.modbus.tcp.enabled=true");
        }
        return R.ok(modbusManager.readHoldingRegisterTcp(slaveId, offset));
    }

    @GetMapping("/rtu/readHolding")
    public R<Number> rtuRead(@RequestParam int slaveId, @RequestParam int offset,
                              @RequestParam(required = false) Long deviceId,
                              @RequestParam(required = false) Long productId,
                              @RequestParam(required = false) String portName,
                              @RequestParam(required = false) Integer baudRate,
                              @RequestParam(required = false) Integer dataBits,
                              @RequestParam(required = false) Integer stopBits,
                              @RequestParam(required = false) Integer parity,
                              @RequestParam(required = false) Integer timeoutMs) throws Exception {
        if (StringUtils.hasText(portName)) {
            int br = defaultIfNull(baudRate, 9600);
            int db = defaultIfNull(dataBits, 8);
            int sb = defaultIfNull(stopBits, 1);
            int pa = defaultIfNull(parity, 0);
            int tm = defaultIfNull(timeoutMs, 2000);
            return R.ok(modbusManager.readHoldingRegisterRtu(portName, br, db, sb, pa, tm, slaveId, offset));
        }
        Map<String, Object> cfg = resolveModbusRtuConfig(deviceId, productId);
        if (cfg != null) {
            String pn = (String) cfg.getOrDefault("portName", properties.getRtu().getPortName());
            int br = asInt(cfg.get("baudRate"), properties.getRtu().getBaudRate());
            int db = asInt(cfg.get("dataBits"), properties.getRtu().getDataBits());
            int sb = asInt(cfg.get("stopBits"), properties.getRtu().getStopBits());
            int pa = asInt(cfg.get("parity"), properties.getRtu().getParity());
            int tm = asInt(cfg.get("timeoutMs"), properties.getRtu().getTimeoutMs());
            return R.ok(modbusManager.readHoldingRegisterRtu(pn, br, db, sb, pa, tm, slaveId, offset));
        }
        if (!properties.getRtu().isEnabled()) {
            return R.fail("Modbus RTU 未启用, 请在配置 iot.modbus.rtu.enabled=true");
        }
        return R.ok(modbusManager.readHoldingRegisterRtu(slaveId, offset));
    }

    @PostMapping("/tcp/writeHolding")
    public R<Void> tcpWrite(@RequestParam int slaveId, @RequestParam int offset, @RequestParam int value,
                             @RequestParam(required = false) Long deviceId,
                             @RequestParam(required = false) Long productId,
                             @RequestParam(required = false) Integer port,
                             @RequestParam(required = false) String host,
                             @RequestParam(required = false) Integer timeoutMs) throws Exception {
        if (StringUtils.hasText(host) && port != null) {
            int tm = timeoutMs != null ? timeoutMs : 2000;
            modbusManager.writeHoldingRegisterTcp(host, port, tm, slaveId, offset, value);
            return R.ok();
        }
        Map<String, Object> cfg = resolveModbusTcpConfig(deviceId, productId);
        if (cfg != null) {
            String h = (String) cfg.getOrDefault("host", properties.getTcp().getHost());
            int p = asInt(cfg.get("port"), properties.getTcp().getPort());
            int tm = asInt(cfg.get("timeoutMs"), properties.getTcp().getTimeoutMs());
            modbusManager.writeHoldingRegisterTcp(h, p, tm, slaveId, offset, value);
            return R.ok();
        }
        if (!properties.getTcp().isEnabled()) {
            return R.fail("Modbus TCP 未启用, 请在配置 iot.modbus.tcp.enabled=true");
        }
        modbusManager.writeHoldingRegisterTcp(slaveId, offset, value);
        return R.ok();
    }

    @PostMapping("/rtu/writeHolding")
    public R<Void> rtuWrite(@RequestParam int slaveId, @RequestParam int offset, @RequestParam int value,
                             @RequestParam(required = false) Long deviceId,
                             @RequestParam(required = false) Long productId,
                             @RequestParam(required = false) String portName,
                             @RequestParam(required = false) Integer baudRate,
                             @RequestParam(required = false) Integer dataBits,
                             @RequestParam(required = false) Integer stopBits,
                             @RequestParam(required = false) Integer parity,
                             @RequestParam(required = false) Integer timeoutMs) throws Exception {
        if (StringUtils.hasText(portName)) {
            int br = defaultIfNull(baudRate, 9600);
            int db = defaultIfNull(dataBits, 8);
            int sb = defaultIfNull(stopBits, 1);
            int pa = defaultIfNull(parity, 0);
            int tm = defaultIfNull(timeoutMs, 2000);
            modbusManager.writeHoldingRegisterRtu(portName, br, db, sb, pa, tm, slaveId, offset, value);
            return R.ok();
        }
        Map<String, Object> cfg = resolveModbusRtuConfig(deviceId, productId);
        if (cfg != null) {
            String pn = (String) cfg.getOrDefault("portName", properties.getRtu().getPortName());
            int br = asInt(cfg.get("baudRate"), properties.getRtu().getBaudRate());
            int db = asInt(cfg.get("dataBits"), properties.getRtu().getDataBits());
            int sb = asInt(cfg.get("stopBits"), properties.getRtu().getStopBits());
            int pa = asInt(cfg.get("parity"), properties.getRtu().getParity());
            int tm = asInt(cfg.get("timeoutMs"), properties.getRtu().getTimeoutMs());
            modbusManager.writeHoldingRegisterRtu(pn, br, db, sb, pa, tm, slaveId, offset, value);
            return R.ok();
        }
        if (!properties.getRtu().isEnabled()) {
            return R.fail("Modbus RTU 未启用, 请在配置 iot.modbus.rtu.enabled=true");
        }
        modbusManager.writeHoldingRegisterRtu(slaveId, offset, value);
        return R.ok();
    }

    private Map<String, Object> resolveModbusTcpConfig(Long deviceId, Long productId) {
        Map<String, Object> cfg = null;
        // 设备优先
        if (deviceId != null) {
            IotDeviceVo dev = deviceService.queryById(deviceId);
            if (dev != null && StringUtils.hasText(dev.getProtocolConfigOverride())) {
                cfg = Jsons.parseMap(dev.getProtocolConfigOverride());
            }
            if (cfg != null && (cfg.containsKey("host") || cfg.containsKey("port"))) return cfg;
            if (dev != null && dev.getProductId() != null) productId = dev.getProductId();
        }
        if (productId != null) {
            IotProductVo prod = productService.queryById(productId);
            if (prod != null && StringUtils.hasText(prod.getProtocolConfig())) {
                Map<String, Object> p = Jsons.parseMap(prod.getProtocolConfig());
                if (p != null) cfg = p;
            }
        }
        return cfg;
    }

    private Map<String, Object> resolveModbusRtuConfig(Long deviceId, Long productId) {
        // 直接复用 TCP 的解析，字段名相同
        return resolveModbusTcpConfig(deviceId, productId);
    }

    private int asInt(Object v, int def) {
        if (v instanceof Number) return ((Number) v).intValue();
        try { return v != null ? Integer.parseInt(String.valueOf(v)) : def; } catch (Exception e) { return def; }
    }

    private int defaultIfNull(Integer v, int def) { return v != null ? v : def; }

    // 轻量 JSON 工具
    static class Jsons {
        @SuppressWarnings("unchecked")
        static Map<String, Object> parseMap(String json) {
            if (!StringUtils.hasText(json)) return null;
            try {
                com.fasterxml.jackson.databind.ObjectMapper om = new com.fasterxml.jackson.databind.ObjectMapper();
                return om.readValue(json, Map.class);
            } catch (Exception e) {
                return null;
            }
        }
    }
}